﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net;
using Common;
using Site_Extractor;
using KnowFirst.Properties;
using Indexing;
using Server;
using Dal;
using LuceneManager;

namespace KnowFirst
{
	public partial class KnowFirstGUI : Form
	{
		int _updateInterval=1200*1000;
		bool _updateTimerIsWorking, _updateNowIsWorking;
		Timer _updateTimer;
		DateTime _searchBegin;
		BackgroundWorker _downloaderBW, _updaterBW, _searchBW, _deepCrawlBW;
		Action<string> _reportStatus;
        Action<string> _displayResult;
        Server.Server _server; //serves both updaters and downloaders.
		Searching.Searcher _searcher;
		List<Site> _sites;
		bool _searching;
        public event EventHandler<Notifying.NotifyEventArgs> NotifyEvent;
        object _updateLock = new object();
        Indexer _indexer;
		List<GenericForum> _forums;
        string _indexPath;

        public KnowFirstGUI()
        {
            InitializeComponent();
            _reportStatus = new Action<string>(_ReportStatus);
            _displayResult = new Action<string>(_DisplayResults);
            _updateTimer = new Timer();
            _updateTimer.Interval = _updateInterval;
            //_updateTimer.Tick += new EventHandler(_updateTimer_Tick);
            _indexPath = Settings.Default.PathToIndex;
            UpdateDatabaseInfo();
            _searcher = new Searching.Searcher();
            LoadAllSites();
			LoadAllForums();
            //var sites = XmlClassHandler<Site[]>.Load(Settings.Default.PathToSitesXML);
            _server = new Server.Server(new List<Site>(_sites), _forums, Settings.Default.ConnectionString,
             Settings.Default.PathToPrefixFile, Settings.Default.PathToIndex);
            //_server._notifier.NotificationEvent += new EventHandler<Notifying.NotifyEventArgs>(onNotifyingEvent);
            _server.ServerEvent += new ServerEventDelegate(_server_ServerEvent);

        }

        void _server_ServerEvent(ServerEventArgs e)
        {
            //ReportStatus("Server " + e._actionType + " done for site " + e._siteName);
        }

        public void LoadAllSites()
        {
            //Load from sites.xml
            //var sites = XmlClassHandler<Site[]>.Load(Settings.Default.PathToSitesXML);
            _sites = new List<Site>();
            //Load from Sites directory.
            string [] fileEntries = Directory.GetFiles(Settings.Default.SiteXMLDirectory,
                "*", SearchOption.TopDirectoryOnly);
            
            foreach (string fileName in fileEntries)
            {
                var site = XmlClassHandler<Site>.Load(fileName);
                _sites.Add(site);
            }
            Dal.DatabaseAbstractionLayer dal = new DatabaseAbstractionLayer(); //no con string needed.
            dal.UpdateSites(_sites);
        }

		public void LoadAllForums()
		{
			_forums = new List<GenericForum>();
			string[] fileEntries = Directory.GetFiles(Settings.Default.ForumsXmlDirectory,
				"*", SearchOption.TopDirectoryOnly);
			foreach (string filename in fileEntries)
			{
				var f = GenericForum.LoadFromXml(filename);
				_forums.Add(f);
			}
			// TODO: DAL part ?
		}


        public void onNotifyingEvent(Object sender, Notifying.NotifyEventArgs args)
        {
            args.Documents = Document.SortDocumentsByPriorityDate(args.Documents);
            foreach (Document doc in args.Documents)
            {
                DisplayResult(args.User.UserName + " " + doc.Fields[0].Text);
            }
        }



		private void DownloadNow()
		{
			//if (_updateNowIsWorking) return;
            ReportStatus("Starting Download...");
			_updateNowIsWorking = true;
			btnStartDownloader.Enabled = false;
			_downloaderBW = new BackgroundWorker();
            _downloaderBW.WorkerReportsProgress = true;
            _downloaderBW.WorkerSupportsCancellation = true;
            _downloaderBW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_downloadBW_RunWorkerCompleted);
            _downloaderBW.DoWork += new DoWorkEventHandler(_downloadBW_DoWork);
            _downloaderBW.ProgressChanged += new ProgressChangedEventHandler(_downloadBW_ProgressChanged);
            _downloaderBW.RunWorkerAsync();	
		}
        private void DeepCrawl()
        {
            //if (_updateNowIsWorking) return;
            ReportStatus("Starting Deep Crawl...");
            _updateNowIsWorking = true;
            btnStartDeepCrawl.Enabled = false;
            _deepCrawlBW = new BackgroundWorker();
            _deepCrawlBW.WorkerReportsProgress = true;
            _deepCrawlBW.WorkerSupportsCancellation = true;
            _deepCrawlBW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_downloadBW_RunWorkerCompleted);
            _deepCrawlBW.DoWork += new DoWorkEventHandler(_deepCrawlBW_DoWork);
            _deepCrawlBW.ProgressChanged += new ProgressChangedEventHandler(_downloadBW_ProgressChanged);
            _deepCrawlBW.RunWorkerAsync();
        }

        private void UpdateNow()
        {
            //if (_updateNowIsWorking) return;
            ReportStatus("Starting Update...");
            _updateNowIsWorking = true;
            btnStartUpdater.Enabled = false;
            _updaterBW = new BackgroundWorker();
            _updaterBW.WorkerReportsProgress = true;
            _updaterBW.WorkerSupportsCancellation = true;
            //_updaterBW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_downloadBW_RunWorkerCompleted);
            _updaterBW.DoWork += new DoWorkEventHandler(_updateBW_DoWork);
            //_updaterBW.ProgressChanged += new ProgressChangedEventHandler(_downloadBW_ProgressChanged);
            _updaterBW.RunWorkerAsync();
        }

        void _downloadBW_ProgressChanged(object sender, ProgressChangedEventArgs e)
		{
			
			UpdateDatabaseInfo();
		}

        void _downloadBW_DoWork(object sender, DoWorkEventArgs e)
        {
            int totalSites, currentSite;

            while (true) //infinite download loop
            {
                ReportStatus("Running Server Downloader...");
                _server.DoDownloadCycle(DateTime.Now.Subtract(new TimeSpan(168,0,0)));
            }
        }

        void _deepCrawlBW_DoWork(object sender, DoWorkEventArgs e)
        {
            int totalSites, currentSite;

            while (true) //infinite download loop
            {
                ReportStatus("Running Server Deep Crawler...");
                _server.DoDeepCrawlCycle(DateTime.MinValue);
            }
        }

        void _downloadBW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			var links = e.Result as RssLink[];
			ReportStatus(string.Format("Update Done."));
			btnStartDownloader.Enabled = true;
			_updateNowIsWorking = false;
		}


		private void btnStartDownloader_Click(object sender, EventArgs e)
		{
			DownloadNow();
		}

        private void btnStopDownloader_Click(object sender, EventArgs e)
        {
            _downloaderBW.Dispose();
            btnStartDownloader.Enabled = true;
            ReportStatus("Downloader Stopped.");

        }

        private void btnStartUpdater_Click(object sender, EventArgs e)
        {
            UpdateNow();
        }

        private void btnStopUpdater_Click(object sender, EventArgs e)
        {
            _updaterBW.Dispose();
            btnStartUpdater.Enabled = true;
            ReportStatus("Updater Stopped.");
        }
        private void btnStartDeepCrawl_Click(object sender, EventArgs e)
        {
            DeepCrawl();
        }

        private void btnStopDeepCrawl_Click(object sender, EventArgs e)
        {

        }


        void _updateBW_DoWork(object sender, DoWorkEventArgs e)
        {
            int totalSites, currentSite;

            while (true) //infinite update loop
            {
                ReportStatus("Running Server Updater...");
                _server.DoUpdateCycle(DateTime.Now.Subtract(new TimeSpan(168, 0, 0)));
            }
        }

	

		#region Status

        private void _DisplayResults(string result)
        {
            searchListBox.Items.Add(result);
        }
		private void _ReportStatus(string status)
		{ 
			statusTextBox.AppendText(string.Format("{0}: {1}\r\n",DateTime.Now.ToString(), status));
		}
		private void ReportStatus(string status)
		{
			Invoke(_reportStatus, status);
		}
		private void ReportError(string error)
		{
			Invoke(_reportStatus, string.Format("Error: {0}", error));
		}

        private void DisplayResult(string result)
        {
            Invoke(_displayResult, result);
        }

		#endregion
        
		private Document[] RunSearch(string term)
		{
            
			try
			{
				ReportStatus(string.Format("Running search for {0}", term));
				List<Document> docs = new List<Document>();
                LuceneManager.LuceneReader lm = new LuceneManager.LuceneReader(
                    new IndexNode[] { new IndexNode(_indexPath) } );
                int temp;
                docs = lm.GetDocumentsMatchingQuery(term,-1,-1, out temp);
				return docs.ToArray();
			}
			catch (Exception ex) { ReportError(ex.Message); return null; }
          
      
        
		}
         
		private void PutResults(Document[] docs)
		{
			searchListBox.Items.Clear();
			foreach (Document doc in docs) searchListBox.Items.Add(doc);
		}

		private void searchTextBox_TextChanged(object sender, EventArgs e)
		{
			SearchButtonEnforce();
		}

		private void SearchButtonEnforce()
		{
			searchButton.Enabled = (searchTextBox.Text != string.Empty) && !_searching;
		}

		private void searchButton_Click(object sender, EventArgs e)
		{
			searchButton.Enabled = false;
            _searchBegin = DateTime.Now;
			_searchBW = new BackgroundWorker();
			_searchBW.WorkerReportsProgress = false;
			_searchBW.WorkerSupportsCancellation = false;
			_searchBW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_searchBW_RunWorkerCompleted);
			_searchBW.DoWork += new DoWorkEventHandler(_searchBW_DoWork);
			_searchBW.RunWorkerAsync(searchTextBox.Text);

           
	
		}

		void _searchBW_DoWork(object sender, DoWorkEventArgs e)
		{
			string term = e.Argument as string;
			var results = RunSearch(term);
			e.Result = results;
		}

		void SearchDone(Document[] docs)
		{

			TimeSpan time = DateTime.Now - _searchBegin;
			if (docs == null) { ReportStatus("(null results)"); docs = new Document[0]; }
			var results = docs;
			ReportStatus(string.Format("Search done, found {0} result(s)", results.Length));
			if (_searchBegin != null) 
			{
				ReportStatus(string.Format("Search took {0:D2}:{1:D2}:{2:D3}", time.Minutes, time.Seconds, time.Milliseconds));
			}
			PutResults(results);
			SearchButtonEnforce();
		}

		void _searchBW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			var results = e.Result as Document[];
			SearchDone(results);
		}

		private void searchListBox_SelectedIndexChanged(object sender, EventArgs e)
		{
			object item = searchListBox.SelectedItem;
			if (item == null || !(item is Document)) return;
			var doc = item as Document;
			docTitleRichTextBox.Text = doc.Fields[(int)Document.Section.Title].Text;
            docSummaryRichTextBox.Text = doc.Fields[(int)Document.Section.Summary].Text;
            docTextRichTextBox.Text = doc.Fields[(int)Document.Section.Text].Text;
            docTimeRichTextBox.Text = doc.DateTime.ToString();
            rtbUrl.Text = doc.url.ToString();
			if (selectTextBox.Text == string.Empty) return;
			SelectTermInRichTextBoxes(selectTextBox.Text);
		}

		private void MarkSelectedWord(string term, RichTextBox tb)
		{			
			int i = 0;
			i = tb.Find(term, i, RichTextBoxFinds.None);
			while (i != -1)
			{
				tb.Select(i, term.Length);
				tb.SelectionColor = Color.White;
				tb.SelectionBackColor = Color.Blue;
				i = tb.Find(term, i + term.Length, RichTextBoxFinds.None);
			}
		}

		private void SelectTermInRichTextBoxes(string term)
		{
			foreach (Control c in splitContainer1.Panel1.Controls)
			{
				if (!(c is RichTextBox)) continue;
				var tb = c as RichTextBox;
				MarkSelectedWord(term, tb);
			}
		}

		private void selectButton_Click(object sender, EventArgs e)
		{
			SelectTermInRichTextBoxes(selectTextBox.Text);
		}

		private void _UpdateDatabaseInfo()
		{
			//int docs = Catalog.NumberOfDocumentsIndexed();			
			//infoLabel.Text = string.Format("{0} document(s)", docs);
		}
		private void UpdateDatabaseInfo()
		{
			try
			{
				Invoke(new Action(_UpdateDatabaseInfo));
			}
			catch { _UpdateDatabaseInfo(); }
		}

		private void KnowFirstGUI_FormClosing(object sender, FormClosingEventArgs e)
		{
			
		}

        private void button1_Click(object sender, EventArgs e)
        {
            _indexer.LoadDbToCatalog();

        }

        private void btnClearMainIndex_Click(object sender, EventArgs e)
        {
            LuceneManager.LuceneManager lm = new LuceneManager.LuceneManager(true, 
                false, Settings.Default.PathToPrefixFile, Settings.Default.PathToIndex,
                 Settings.Default.PathToIndex);
            lm.CloseIndexWriter();
        }

        private void btnClearUpdateCycle_Click(object sender, EventArgs e)
        {
            DatabaseAbstractionLayer dal = new DatabaseAbstractionLayer();
            dal.DeleteUpdateCycleData();
        }

       
       
        

	}        
}
